/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.mzk.oai.metadataRepository.repository.xmlConfiguration;

import cz.mzk.oai.metadataRepository.metadata.MetadataFormat;
import cz.mzk.oai.metadataRepository.transformation.XsltConfig;
import java.io.File;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.xml.transform.TransformerConfigurationException;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.XPath;

/**
 *
 * @author Martin Rehanek (rehan at mzk.cz)
 */
class CollectionConfigLoader extends CollectionsCommonConfigLoader {

    private static int MAX_ALEPH_BASE_LENGTH = 5;
    private XPath collectionsXpath = DocumentHelper.createXPath("/CollectionsAndSetsMapping/Collections/Collection");
    private XPath nameXpath = DocumentHelper.createXPath("@name");
    private XPath metadataDirXpath = DocumentHelper.createXPath("MetadataDir");
    private XPath recordsXpath = DocumentHelper.createXPath("Records");
    private XPath transformationsXpath = DocumentHelper.createXPath("Transformation");
    private XPath setReferenceXpath = DocumentHelper.createXPath("InSets/SetReference/@id");
    private XPath alephBaseXpath = DocumentHelper.createXPath("AlephBase");
    private XPath itemsCanBeTransferedXpath = DocumentHelper.createXPath("@itemsCanBeTransfered");
    private final Set<CollectionConfig> collections = new HashSet<CollectionConfig>();
    private final Set<String> collectionNamesFound = new HashSet<String>();

    public CollectionConfigLoader(Document configDoc) throws DocumentException {
        super(configDoc);
        loadCollections();
    }

    Set<CollectionConfig> getResults() {
        return collections;
    }

    private void loadCollections() throws DocumentException {
        try {
            List<Element> collectionEls = collectionsXpath.selectNodes(configDoc);
            //System.out.println("collections:" + collectionEls.size());
            for (Element collectionEl : collectionEls) {
                CollectionConfig collection = loadCollectionConfig(collectionEl);
                addCollectionConfig(collection);
            }
        } catch (Exception e) {
            throw new DocumentException(e);
        }
    }

    private CollectionConfig loadCollectionConfig(Element collectionEl) throws Exception {
        String name = loadCollectionName(collectionEl);
        checkIfUnique(name);
        File metadataDir = loadMetadataDir(collectionEl);
        //System.err.println("metadataDir:" + metadataDir.getAbsolutePath());
        Set<MetadataFormat> metadataFormats = loadCollectionMetadataFormats(collectionEl);
        Set<XsltConfig> xsltConfigs = loadCollectionTransformations(collectionEl);
        Set<Integer> setIds = loadSetIds(collectionEl);
        String alephBase = loadAlephBase(collectionEl);
        boolean itemsCanBeMoved = loadItemsCanBeTransfered(collectionEl);
        return new CollectionConfig(name, metadataDir, metadataFormats, xsltConfigs, setIds, alephBase, itemsCanBeMoved);
    }

    private String loadCollectionName(Element collectionEl) {
        Attribute nameAtr = (Attribute) nameXpath.selectSingleNode(collectionEl);
        return nameAtr.getText();
    }

    private void checkIfUnique(String name) throws Exception {
        if (collectionNamesFound.contains(name)) {
            throw new Exception("name '" + name + "' already present");
        }
    }

    private File loadMetadataDir(Element collectionEl) {
        Element metadataDirEl = (Element) metadataDirXpath.selectSingleNode(collectionEl);
        String dirName = metadataDirEl.getTextTrim();
        return new File(dirName);
    }

    private Set<MetadataFormat> loadCollectionMetadataFormats(Element collectionEl) {
        Element recordsEl = (Element) recordsXpath.selectSingleNode(collectionEl);
        return loadMetadataFormats(recordsEl);
    }

    private Set<XsltConfig> loadCollectionTransformations(Element collectionEl) throws TransformerConfigurationException {
        List<Element> transformations = transformationsXpath.selectNodes(collectionEl);
        return loadTransformations(transformations);
    }

    private Set<Integer> loadSetIds(Element collectionEl) {
        List<Attribute> idAttributes = setReferenceXpath.selectNodes(collectionEl);
        Set<Integer> setIds = new HashSet<Integer>(idAttributes.size());
        for (Attribute idAttribute : idAttributes) {
            Integer setId = loadSetReference(idAttribute);
            setIds.add(setId);
        }
        return setIds;
    }

    private Integer loadSetReference(Attribute idAttribute) {
        String stringValue = idAttribute.getText();
        return Integer.parseInt(stringValue);
    }

    private void addCollectionConfig(CollectionConfig collection) {
        collection.addMetadataFormats(commonConfig.getMetadataFormats());
        collection.addTransformations(commonConfig.getTransformationConfigs());
        collections.add(collection);
        collectionNamesFound.add(collection.getName());
    }

    private String loadAlephBase(Element collectionEl) throws Exception {
        Element alephBaseEl = (Element) alephBaseXpath.selectSingleNode(collectionEl);
        String alephBase = alephBaseEl.getTextTrim();
        checkAlephBaseNotToLong(alephBase);
        return alephBase;
    }

    private void checkAlephBaseNotToLong(String alephBase) throws Exception {
        if (alephBase.length() > MAX_ALEPH_BASE_LENGTH) {
            throw new Exception("aleph base '" + alephBase + "' is to long");
        }
    }

    private boolean loadItemsCanBeTransfered(Element collectionEl) throws Exception {
        Attribute attr = (Attribute) itemsCanBeTransferedXpath.selectSingleNode(collectionEl);
        if (attr == null) { //attribute missing
            return true;
        } else {
            String value = attr.getValue();
            if ("true".equals(value)) {
                return true;
            } else if ("false".equals(value)) {
                return false;
            } else {
                throw new Exception("Illegal value of attribute " + itemsCanBeTransferedXpath.getText() + ": '" + value + "'");
            }
        }
    }
}
